package dpss.server;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.xml.sax.SAXException;

import dpss.commons.CustomLogger;
import dpss.commons.Player;
import dpss.commons.Repo;

/**
 * The basic class to start the server
 * 
 * @author musab.mirza
 * 
 */
public class Server {

	private static CustomLogger logger = CustomLogger.getInstance();
	private static final String FILE_PROPERTIES = "project.properties";
	private static Properties prop = null;
	private static AccountDatabase accountDatabase;
	private static Repo repo;
	

	/**
	 * Start of the program
	 * 
	 * @param args
	 * @throws IOException 
	 * @throws SAXException 
	 * @throws ParserConfigurationException 
	 */
	public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
		
		logger.logConsole(true);

		loadProperties();
		logger.log("\n************************************************\n" + prop.getProperty("server.name") + " started!\n************************************************\n");
		
		repo = new Repo(prop.getProperty("repo.path"));
		
		updateRepo();
		
		TestData testData = new TestData();
		HashSet<Player> playersList = testData.getTestPlayers(prop.getProperty("server.iprange"));
		
		accountDatabase = new AccountDatabase();
		accountDatabase.loadTestPlayers(playersList);
		
		bindRegistry();
		setupUDPListener();
	}

	/**
	 * Loads the properties file into the cache
	 */
	private static void loadProperties() {
		prop = new Properties();
		InputStream input = null;
		try {
			input = new FileInputStream(FILE_PROPERTIES);
			prop.load(input);
		} catch (IOException ex) {
			logger.log("ERROR - Unable to update repo!");
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}


	/**
	 * Starts the UDP listener thread
	 */
	private static void setupUDPListener() {
		try {
			UDPListenerThread UDPListener = new UDPListenerThread(accountDatabase, Integer.parseInt((String) prop.getProperty("server.udpport")));
			UDPListener.start();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (SocketException e) {
			logger.log("ERROR - Could not start UDP listener on port " + prop.getProperty("server.udpport"));
			System.exit(0);
		}
		
	}


	/**
	 * Starts the server and binds it
	 */
	private static void bindRegistry() {
		
		String bindName = "GameServer_" + prop.getProperty("server.id");

		try {
			LocateRegistry.createRegistry(1099);
		} catch (RemoteException e1) {
			// If the registry is already running do nothing
		}
		
		
		try {
			LocateRegistry.getRegistry().lookup(bindName);
		} catch (AccessException e1) {
			e1.printStackTrace();
		} catch (RemoteException e1) {
			e1.printStackTrace();
		} catch (NotBoundException e1) {
			
			// Continue if the name is not bound
			DpssApiImpl gameServer = null;
			try {
				gameServer = new DpssApiImpl(accountDatabase);
			} catch (RemoteException e) {
				logger.log("ERROR - Remote exception! CASE1");
				e.printStackTrace();
			}
			try {
				Naming.bind(bindName, gameServer);
			} catch (MalformedURLException e) {
				logger.log("ERROR - Malformed url!");
				e.printStackTrace();
			} catch (RemoteException e) {
				logger.log("ERROR - Remote exception!");
				e.printStackTrace();
			} catch (AlreadyBoundException e) {
				logger.log("ERROR - Already bound exception!");
				e.printStackTrace();
			}
			logger.log("RMI setup as 'GameServer_" + prop.getProperty("server.id") + "' on localhost");
			return;
		}
		

		logger.log("ERROR - " + bindName + " is already bound on the current RMI registry. Please use a different bind name");
		System.exit(0);

		
	}

	/**
	 * Loads information from the properties file and writes it to the central
	 * repository
	 * @throws TransformerException 
	 * @throws IOException 
	 * @throws SAXException 
	 * @throws ParserConfigurationException 
	 */
	private static void updateRepo() {

		// Building the map
		Map<String, String> serverInfo = new HashMap<String, String>();
		serverInfo.put("id", prop.getProperty("server.id"));
		serverInfo.put("name", prop.getProperty("server.name"));
		serverInfo.put("udpport", prop.getProperty("server.udpport"));
		serverInfo.put("iprange", prop.getProperty("server.iprange"));
		try {
			repo.setServerInfo(serverInfo);
		} catch (ParserConfigurationException | SAXException | IOException
				| TransformerException e) {
			logger.log("ERROR - Unable to update the repo");
			e.printStackTrace();
			System.exit(0);
		}
		logger.log("Repo updated with server info");
	}
	
	/**
	 * Returns the current server's properties
	 * @return
	 */
	public static Properties getServerProperties(){
		return prop;
	}

	/**
	 * Returns the repo object's instance
	 * @return
	 */
	public static Repo getRepo() {
		return repo;
	}

}
